<template>
    <div>
        <el-table
                v-if="!lastError.isError"
                :data="tableData"
                stripe
                v-loading="loading"
                element-loading-text="加载中"
                element-loading-background="rgba(245, 245, 245, 0.9)"
                @row-click="rowClicked"
                class="table"
        >
            <slot></slot>
        </el-table>
        <table-error
                v-else
                v-bind="lastError"
                :title="title"
                @handle-error="activeLoad(currentPageMeta,currentArgs)"
        ></table-error>
    </div>
</template>

<script>
    import TableError from "~/components/file-list/base-file-list/TableError";
    const DATA_UPDATE_SIGNAL = "data-update";
    const ROW_CLICKED_EVENT = "row-clicked";
    /**
     *  组件将会被动地被${DATA_UPDATE_SIGNAL}信号激活，调用tryLoad函数获取所需数据,并处理相关异常。 <br>
     *      this.$refs.FileTable.$emit('data-update', pageMeta:{id, page}, ...args)
     *          - 在触发信号时，请传入对象包含目标文件夹的id和页码（主要作为异步函数执行时的记录）
     *          - pageMeta参数后可以接上任意多个参数，将会被传给tryLoad。
     *          - 可以将参数写入pageMeta对象。
     */
    export default {
        name: "FileTable",
        components: {TableError},
        props: {
            /**
             * 数据载入函数 <br>
             *      - 必须满足函数幂等性。
             *      - 应当返回一个Promise<T>。
             *      - Promise<T>返回除了数组以外的任何值，都视为页错误。
             *      - 当Promise<T>抛出Error或返回一个字符串时，错误信息将被填充。
             *
             * 函数形式： <br>
             *     async function tryLoad:Promise<[]> (pageMeta:{id,page}, ...args)
             *
             * 介绍：<br>
             *     保证此函数对于相同的 pageMeta，同一时间只会调用一遍。对于不同的pageMeta，最后调用的将会生效。
             */
            tryLoad: {
                type: Function,
                default: ()=>{
                    return "数据载入接口未定义。";
                }
            },
            pageSize: {
                type: Number,
                default: 12
            },
            title: {
                type: String,
                default: "",
            },
            tableData: {
                type: Array,
                require: true,
            },
        }, data(){
            return{
                lastError: {
                    isError: false,
                    errorMessage: "",
                },
                loading: false,
                currentPageMeta: {
                    id: null,
                    page: null,
                },
                currentArgs: [],
                invokeRecord: [],
            };
        },
        methods:{
            rowClicked(row,col,event){
                this.$emit(ROW_CLICKED_EVENT,row,col,event);
            },
            async activeLoad(pageMeta, ...args) {
                // 参数格式错误
                if (typeof pageMeta !== "object" || !pageMeta.id || !pageMeta.page) return undefined;
                // 已存在相同任务正在执行中，直接结束当前任务
                if (this.invokeRecord.some(record => this.metaEquals(pageMeta, record))) return undefined;
                this.loading = true;
                this.invokeRecord.push(pageMeta);
                this.currentPageMeta = pageMeta;
                this.currentArgs = args;
                try {
                    const loadAns = await this.tryLoad(pageMeta, ...args);
                    // 此任务已过期，停止执行
                    if(! this.metaEquals(this.currentPageMeta,pageMeta)) return undefined;
                    if(loadAns instanceof Array){
                        this.clearError();
                        if(loadAns.length>this.pageSize){
                            loadAns.length = this.pageSize;
                            console.warn(`文件列表超过限制！期望：${this.pageSize} , 实际：${loadAns.length}`);
                        }
                        this.$emit("update:tableData",loadAns);
                        this.loading = false;
                    } else if(typeof loadAns === "string"){
                        this.createPageError(loadAns);
                    }else {
                        this.createPageError("前端代码执行异常，文件列表获取到了错误的返回值类型。");
                    }
                } catch (e) {
                    this.createPageError(e.message);
                } finally {
                    this.invokeRecord = this.invokeRecord.filter(record => !this.metaEquals(pageMeta, record));
                }
            },
            metaEquals(a,b){
                return a.id===b.id && a.page===b.page;
            },
            createPageError(msg){
                if(typeof msg==="string"){
                    this.lastError.errorMessage = msg;
                }
                this.lastError.isError = true;
            },
            clearError(){
                this.lastError.errorMessage = "";
                this.lastError.isError = false;
            },
            /**
             * 格式化毫秒时间戳
             */
            msFormat(ms){
                return moment(ms).format('YYYY-MM-DD HH:mm:ss');
            },
        },
        mounted() {
            this.$on(DATA_UPDATE_SIGNAL,this.activeLoad);
        }
    }
</script>

<style scoped>
</style>